#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include "log.hpp"

struct TcpSocket
{
    std::string ip_;
    uint16_t port_;
    int sockfd_;
    bool exit_;
    TcpSocket() = default;
    TcpSocket(std::string ip, uint16_t port, int fd)
        : ip_(ip), port_(port),sockfd_(fd), exit_(false)
    {
    }
};

namespace std
{
    template <>
    struct hash<TcpSocket>
    {
        size_t operator()(const TcpSocket &socket) const
        {
            return std::hash<int>()(socket.sockfd_);
        }
    };
}

bool operator==(const TcpSocket &lhs, const TcpSocket &rhs)
{
    return lhs.sockfd_ == rhs.sockfd_;
}

// 将文件操作设置为非阻塞
void setNonBlock(int fd)
{
    // 获得文件描述符的标志  open(src_path,O_APPEND | O_CREATE);
    int flags = fcntl(fd, F_GETFL);
    if (flags < 0)
    {
        perror("fcntl");
        return;
    }
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

class Epoll
{
public:
    Epoll(int listenfd)
    {
        listenfd_ = listenfd;
        epoll_fd = epoll_create(10);
    }
    ~Epoll()
    {
        close(epoll_fd);
    }

    bool Add(const TcpSocket socket)
    {
        // 添加索引
        indexMap[socket.sockfd_] = socket;
        epoll_event event;
        event.data.fd = socket.sockfd_;
        // EPOLLIN: 可以对该文件执行读操作
        // EPOLLOUT 可以对该文件执行写操作
        // EPOLLERR：表示对应文件描述符上出现了错误。
        // EPOLLHUP：poll:表示对应文件描述符上出现了挂起事件，如连接断开或对端关闭连接。
        // EPOLLRDHUP：epoll:表示对应文件描述符上的连接已被对端关闭或重置。
        // EPOLLET：表示使用边缘触发模式。
        // EPOLLONESHOT：表示使用一次性触发模式。
        event.events = EPOLLIN;
        // int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
        // op： EPOLL_CTL_ADD 注册新的fd  EPOLL_CTL_MOD 修改fd的事件 EPOLL_CTL_DEL 删除epfd中的fd
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket.sockfd_, &event) < 0)
        {
            perror("epoll add fd");
            return false;
        }
        log(DEBUG, "Epoll Add [%d]", socket.sockfd_);
        return true;
    }

    bool Del(const int &sockfd) const
    {
        if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sockfd, nullptr) < 0)
        {
            perror("epoll del fd");
            return false;
        }
        return true;
    }

    bool Wait(std::vector<TcpSocket> &output)
    {
        output.clear();
        // 存储就绪事件对应的fd
        epoll_event Events[100];
        // 返回就绪事件的数量
        int nfds = epoll_wait(epoll_fd, Events, sizeof(Events), -1);
        if (nfds < 0)
        {
            perror("epoll_wait");
            return false;
        }
        if (nfds == 0)
        {
            return false;
        }
        for (int i = 0; i < nfds; i++)
        {
            // 对端退出
            if (Events[i].data.fd != listenfd_)
            {
                output.push_back(indexMap[Events[i].data.fd]);
                indexMap.erase(Events[i].data.fd);
                Del(Events[i].data.fd);
            }
            // 对端连接
            else
            {
                output.push_back(indexMap[Events[i].data.fd]);
            }
        }
        return true;
    }

private:
    int epoll_fd;
    int listenfd_;
    std::unordered_map<int, TcpSocket> indexMap;
};